//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.TridentUtilities
{
    #region Using directives

    using System;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Windows;
    using Microsoft.Research.DataLayer;
    using System.Collections.Generic;
    using System.Security.Cryptography;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using System.Text.RegularExpressions;
    using System.Runtime.InteropServices;
    using System.IO.Packaging;
    using System.Globalization;


    #endregion

    /// <summary>
    /// Provides utility functions.
    /// </summary>
    public class Helper
    {
        /// <summary>
        /// Dummy constructor to fix the FX-Cop issue
        /// </summary>
        private Helper()
        {
        }

        /// <summary>
        /// Returns a value indicating whether the specified files is an assembly built for managed .Net framework or not.
        /// </summary>
        /// <param name="filePath">
        /// Path of the file.
        /// </param>
        /// <returns>
        /// True if managed, False otherwise.
        /// </returns>
        public static bool IsManagedAssembly(string filePath)
        {
            bool isManaged = false;

            if (Helper.IsAssemblyFile(filePath))
            {
                FileStream fileStream = null;
                try
                {
                    // Create a byte array to hold the information.
                    byte[] assemblyHeader = new byte[4096];

                    // Open the file
                    fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

                    // Put in the first 4k of the file into our byte array
                    fileStream.Read(assemblyHeader, 0, 4096);

                    // Flush any buffered data.
                    fileStream.Flush();

                    unsafe
                    {
                        // The fixed statement prevents relocation of a variable by the garbage collector.
                        // It pins the location of the assemblyHeader object in memory so that they will not be moved.
                        // The objects will be unpinned when the fixed block completes.
                        fixed (byte* ptrAssemblyHeader = assemblyHeader)
                        {
                            // Get the first 64 bytes and turn it into a IMAGE_DOS_HEADER
                            IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)ptrAssemblyHeader;

                            // Now that we have the DOS header, we can get the offset
                            // (e_lfanew) add it to the original address (ptrAssemblyHeader) and
                            // squeeze those bytes into an IMAGE_NT_HEADERS32 structure.
                            IMAGE_NT_HEADERS32* ntHeader = (IMAGE_NT_HEADERS32*)(dosHeader->e_lfanew + ptrAssemblyHeader);

                            // OptionalHeader.Magic tells whether the assembly is PE32 (0x10b) or PE32+ (0x20b).
                            // PE32+ just means 64-bit. So instead of checking if it is an X64 or Itanium, just check if it's a PE32+.
                            if (ntHeader->OptionalHeader.Magic == 0x20b)
                            {
                                // If it is a PE32+, cast it as an IMAGE_NT_HEADERS64 pointer.
                                // Now check the size if it is greater than zero.
                                if (((IMAGE_NT_HEADERS64*)ntHeader)->OptionalHeader.DataDirectory.Size > 4096)
                                {
                                    // If yes, it is a managed assembly.
                                    isManaged = true;
                                }
                            }
                            else
                            {
                                // This is a 32-bit assembly. Check its size.
                                if (ntHeader->OptionalHeader.DataDirectory.Size > 0)
                                {
                                    isManaged = true;
                                }
                            }
                        }
                    }
                }
                catch
                {
                    // Just return false.
                    isManaged = false;
                }
                finally
                {
                    if (fileStream != null)
                    {
                        fileStream.Close();
                        fileStream.Dispose();
                    }
                }
            }

            return isManaged;
        }

        /// <summary>
        /// Checks if the assembly contains an activity or not.
        /// </summary>
        /// <param name="filePath">
        /// Path of the assembly.
        /// </param>
        /// <returns>
        /// True if there is an activity, False otherwise.
        /// </returns>
        public static bool ContainsActivity(string filePath)
        {
            bool containsActivity = false;
            if (Helper.IsManagedAssembly(filePath))
            {
                try
                {
                    // Find out if this file is an activity DLL or not.
                    Assembly assembly = AssemblyLoad.LoadFrom(filePath);
                    containsActivity = assembly.GetTypes().Any(type => type.IsSubclassOf(typeof(System.Workflow.ComponentModel.Activity)));
                }
                catch (Exception)
                {
                }
            }

            return containsActivity;
        }

        /// <summary>
        /// Gets the filename of the specified file.
        /// </summary>
        /// <param name="filePath">
        /// Path of the file.
        /// </param>
        /// <returns>
        /// File name of the file.
        /// </returns>
        public static string GetFileName(string filePath)
        {
            string fileName = filePath;
            if (Path.IsPathRooted(filePath))
            {
                fileName = Path.GetFileName(filePath);
            }

            return fileName;
        }

        /// <summary>
        /// Returns a value indicating if the specified file is an assembly file or not.
        /// </summary>
        /// <param name="filePath">
        /// Path of the file.
        /// </param>
        /// <returns>
        /// True if assembly, False otherwise.
        /// </returns>
        public static bool IsAssemblyFile(string filePath)
        {
            bool isAssembly = false;
            if (!string.IsNullOrEmpty(filePath))
            {
                isAssembly = filePath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || filePath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase);
            }

            return isAssembly;
        }

        /// <summary>
        /// Returns a value indicating if the speficied file is a workflow file or not.
        /// </summary>
        /// <param name="filePath">
        /// Path of the file.
        /// </param>
        /// <returns>
        /// True if workflow, False otherwise.
        /// </returns>
        public static bool IsWorkflowFile(string filePath)
        {
            bool isWorkflow = false;
            if (!string.IsNullOrEmpty(filePath))
            {
                isWorkflow = filePath.EndsWith(".xoml", StringComparison.OrdinalIgnoreCase);
            }

            return isWorkflow;
        }

        /// <summary>
        /// Recursively builds references for the file selected.
        /// </summary>
        /// <param name="selectedFile">
        /// File selected.
        /// </param>
        /// <param name="currentDirPath">
        /// Selected file's parent directory.
        /// </param>
        public static void BuildReferences(Collection<FileReference> fileReferences, string selectedFile, string currentDirPath, Stack<string> assemblyNamesAlreadyParsed = null)
        {
            if (assemblyNamesAlreadyParsed == null)
                assemblyNamesAlreadyParsed = new Stack<string>();
            Collection<string> references = new Collection<string>();
            if (Helper.IsManagedAssembly(selectedFile))
            {
                Assembly assembly = AssemblyLoad.LoadFrom(selectedFile);
                if (assembly != null)
                {
                    string thisAssemblyName = assembly.GetName().FullName;
                    if (!assemblyNamesAlreadyParsed.Contains(thisAssemblyName))
                    {
                        assemblyNamesAlreadyParsed.Push(thisAssemblyName);
                        AssemblyName[] refAssemblies = assembly.GetReferencedAssemblies();
                        foreach (AssemblyName asmName in refAssemblies)
                        {
                            Assembly tempAssembly = AssemblyLoad.SearchCurrentDomain(asmName.Name);
                            if (tempAssembly == null)
                            {
                                try
                                {
                                    string dllPath = System.IO.Path.Combine(currentDirPath, asmName.Name + ".dll");
                                    string exePath = System.IO.Path.Combine(currentDirPath, asmName.Name + ".exe");
                                    if (File.Exists(dllPath) && Helper.IsManagedAssembly(dllPath))
                                    {
                                        tempAssembly = AssemblyLoad.LoadFrom(dllPath);
                                    }
                                    else if (File.Exists(exePath) && Helper.IsManagedAssembly(exePath))
                                    {
                                        tempAssembly = AssemblyLoad.LoadFrom(exePath);
                                    }
                                    else
                                    {
                                        tempAssembly = Assembly.Load(asmName);
                                    }
                                }
                                catch (Exception)
                                {
                                    // Ignore the error. If tempAssembly is null, it'll be handled by the null check.
                                }
                            }
                            else
                            {

                            }

                            if (tempAssembly != null)
                            {
                                if (!tempAssembly.GlobalAssemblyCache &&
                                    !tempAssembly.Location.Equals(selectedFile, StringComparison.OrdinalIgnoreCase))
                                {
                                    references.Add(tempAssembly.Location);
                                    Helper.BuildReferences(fileReferences, tempAssembly.Location, currentDirPath, assemblyNamesAlreadyParsed);
                                }
                            }
                        }
                        //assemblyNamesAlreadyParsed.Pop( );
                    }
                }
            }

            bool exists = fileReferences.Any(fileRef => fileRef.DisplayName.Equals(Helper.GetFileName(selectedFile), StringComparison.OrdinalIgnoreCase));
            if (!exists)
            {
                FileReference fileRef = new FileReference(selectedFile, references);
                if (fileRef.ContainsActivity)
                {
                    int index = 0;
                    FileReference lastActivity = fileReferences.LastOrDefault(F => F.ContainsActivity);
                    if (lastActivity != null)
                    {
                        index = fileReferences.IndexOf(lastActivity) + 1;
                    }

                    fileReferences.Insert(index, fileRef);
                }
                else
                {
                    fileReferences.Add(fileRef);
                }
            }
        }


        /// <summary>
        /// Get the assembly images from the registry depending on the file extension.
        /// </summary>
        /// <param name="fileExtension">The file extension.</param>
        /// <param name="connection">The connection object.</param>
        /// <returns>Collection of assembly metadata.</returns>
        public static Collection<AssemblyMetadata> GetAssembliesFromRegistry(string fileExtension, Connection connection)
        {
            Collection<AssemblyMetadata> assemblies = new Collection<AssemblyMetadata>();
            try
            {
                // Search the assembly images for images of the same extension.
                Collection<AssemblyImage> assemblyImages = AssemblyImage.CommonSearches.GetAssemblyImagesByExtension(fileExtension, connection);
                if (assemblyImages != null)
                {
                    foreach (AssemblyImage image in assemblyImages)
                    {
                        if (image.IsLatestVersion())
                        {
                            assemblies.Add(new AssemblyMetadata()
                            {
                                Name = image.Filename,
                                ImportedBy = image.Created.User,
                                ImportedTime = image.Created.When.ToString(),
                                Id = (image as IObject).ID,
                                DisplayName = image.Name
                            });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(ex, TridentErrorConstants.ErrorNumber7000001, TridentResourceManager.GetString("AssemblyImageRetriveError"));
            }
            return assemblies;
        }

        /// <summary>
        /// Creates a checksum of the file contents.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <returns>The checksum string.</returns>
        public static string CreateChecksum(byte[] fileContents)
        {
            SHA512 sha512 = SHA512.Create();
            byte[] result = sha512.ComputeHash(fileContents);
            return Convert.ToBase64String(result);
        }

        /// <summary>
        /// Read all the bytes from assembly file.
        /// This will be used to save in the Registry.
        /// </summary>
        /// <param name="fileName">Full path of the file which has to converted to byte[].</param>
        /// <returns>Byte[] of the file.</returns>
        public static Byte[] ReadBytesFromAssemblyFile(string fileName)
        {
            Byte[] buff = null;

            try
            {
                using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                {
                    using (BinaryReader br = new BinaryReader(fs))
                    {
                        long numBytes = new FileInfo(fileName).Length;
                        buff = br.ReadBytes((int)numBytes);
                    }
                }
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    ex,
                    TridentErrorConstants.ErrorNumber7000002,
                    ex.Message);
            }

            return buff;
        }

        /// <summary>
        /// Check if the file is already present in registry.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <param name="registryConnection">The registry connection</param>
        /// <returns></returns>
        public static void GetImageFromRegistry(FileReference fileReference, Connection registryConnection)
        {
            Guid result = Guid.Empty;

            try
            {
                // Read the file contents and create checksum.
                byte[] fileContent = Helper.ReadBytesFromAssemblyFile(fileReference.FilePath);
                string checkSum = Helper.CreateChecksum(fileContent);
                fileReference.Checksum = checkSum;

                // Search for the checksum in the registry.
                Collection<AssemblyImage> searchResult =
                    AssemblyImage.CommonSearches.GetAssemblyImagesByChecksum(checkSum, registryConnection);

                // Return the Id of the Assembly image with the same checksum.
                if (searchResult != null && searchResult.Count > 0)
                {
                    result = (searchResult[0] as IObject).ID;
                }

                if (result.Equals(Guid.Empty))
                {
                    fileReference.CopyExistsInRegistry = false;
                }
                else
                {
                    fileReference.CopyExistsInRegistry = true;
                    fileReference.AssemblyImageIdOfCopy = result;
                }
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    ex,
                    TridentErrorConstants.ErrorNumber7000003,
                    ex.Message);
            }
        }

        /// <summary>
        /// Determines whether the specified connection is valid or not.
        /// Logic:
        /// </summary>
        /// <param name="connection">The connection.</param>
        /// <returns>
        /// 	<c>true</c> if specified connection is valid; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsValidConnection(Connection connection)
        {
            bool isValidConenction = false;
            if (CheckValidSystemUser(connection)
                && CheckValidSystemPolicy(connection))
            {
                isValidConenction = true;
            }

            return isValidConenction;
        }

        /// <summary>
        /// Checks the valid system user.
        /// </summary>
        /// <param name="connection">The connection.</param>
        /// <returns></returns>
        private static bool CheckValidSystemUser(Connection connection)
        {
            bool isValidUser = false;

            try
            {
                User systemUser = GetSystemUser(connection);

                isValidUser = systemUser != null;
            }
            catch (Exception)
            {
                isValidUser = false;
            }

            return isValidUser;
        }

        /// <summary>
        /// Checks whether the system policy is valid or not.
        /// </summary>
        /// <param name="connection">The connection.</param>
        /// <returns></returns>
        public static bool CheckValidSystemPolicy(Connection connection)
        {
            bool isValidPolicy = false;

            try
            {
                SystemPolicy policy = SystemPolicy.Get(connection);

                isValidPolicy = policy != null;
            }
            catch (Exception)
            {
                isValidPolicy = false;
            }

            return isValidPolicy;
        }

        /// <summary>
        /// Refreshes the system policy.
        /// </summary>
        /// <param name="connection">The connection.</param>
        public static void RefreshSystemPolicy(Connection connection)
        {
            try
            {
                SystemPolicy policy = SystemPolicy.Get(connection);
                policy.Refresh();
            }
            catch (TridentCustomException)
            {
                throw;
            }
            catch (Exception ex)
            {
                TridentErrorHandler.ProcessKnownExceptionsInBusinessLogic(ex);
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    ex,
                    TridentErrorConstants.ErrorNumber4000087,
                    TridentCommonResourceManager.GetString("SystemPolicyRefreshError"));
            }
        }

        /// <summary>
        /// Get the system user object.
        /// </summary>
        /// <param name="regConn">The registry connection.</param>
        /// <returns>The system user.</returns>
        private static User GetSystemUser(Connection regConn)
        {
            User systemUser = null;
            try
            {
                systemUser = User.Load(Constants.SystemUserName, regConn);
            }
            catch (RecordNotFoundException exp)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exp,
                    TridentErrorConstants.ErrorNumber4000032,
                    TridentResourceManager.GetString("SystemUserNotFound"));
            }
            return systemUser;
        }

        /// <summary>
        /// Check if the workflow name contains valid characters.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>
        /// True if the name contains valid characters; false otherwise
        /// </returns>
        public static bool CheckNameForValidCharacters(string name)
        {
            return Regex.IsMatch(name, @"^([\s\S]+)$");
        }

        /// <summary>
        /// Gets the safe name of the file.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static string GetSafeFileName(string fileName)
        {
            return Regex.Replace(
                fileName,
                "[" + Regex.Escape(new string(Path.GetInvalidFileNameChars())) + "]",
                string.Empty, RegexOptions.Compiled);
        }

        /// <summary>
        /// Extracts the package to a temp directory and returns a collection of file paths.
        /// </summary>
        /// <param name="packageFile">
        /// Package file path.
        /// </param>
        /// <param name="packageTempDirectory">
        /// Temp directory of the package.
        /// </param>
        /// <param name="silentMode">
        /// True if no user messages should be popped up, false otherwise.
        /// </param>
        /// <returns>
        /// Collection of file paths.
        /// </returns>
        public static Collection<string> ExtractPackage(string packageFile, string packageTempDirectory, bool silentMode)
        {
            Collection<string> packageFiles = null;
            if (!string.IsNullOrEmpty(packageFile))
            {
                Package workflowPackage = null;
                try
                {
                    // Open the package file and get its parts.
                    workflowPackage = Package.Open(packageFile, FileMode.Open, FileAccess.Read);
                    if (workflowPackage != null)
                    {
                        // Delete temp directory if it exists.
                        if (Directory.Exists(packageTempDirectory))
                        {
                            Directory.Delete(packageTempDirectory, true);
                        }

                        // Create temp directory.
                        Directory.CreateDirectory(packageTempDirectory);

                        // Get the package parts and extract each one of them.
                        PackagePartCollection partColl = workflowPackage.GetParts();
                        foreach (PackagePart packagePart in partColl)
                        {
                            // Extract the package part to temp directory.
                            Helper.ExtractPart(packagePart, packageTempDirectory);
                        }

                        // Package files are all extracted now.
                        // Create a folder "PackageFiles" to hold all the required files.
                        string destTempPath = Path.Combine(packageTempDirectory, "PackageFiles");
                        Directory.CreateDirectory(destTempPath);

                        // Get a reference to "Trident" directory within the package.
                        DirectoryInfo parentDir = new DirectoryInfo(Path.Combine(packageTempDirectory, "Trident"));

                        // ActivityKind directory contains the XOML file.
                        DirectoryInfo[] activityKindDirs = parentDir.GetDirectories("ActivityKind");
                        if (activityKindDirs != null && activityKindDirs.Length > 0)
                        {
                            DirectoryInfo activityKindDir = activityKindDirs[0];
                            foreach (DirectoryInfo subDir in activityKindDir.GetDirectories())
                            {
                                // "_rels" folder contains the relationship. Skip this,
                                if (!subDir.Name.Equals("_rels", StringComparison.OrdinalIgnoreCase))
                                {
                                    foreach (FileInfo file in subDir.GetFiles())
                                    {
                                        // Copy the XOML file to destination directory.
                                        // Ideally, this loop should execute only once since there can be only one XOML file.
                                        string destFile = Path.Combine(destTempPath, Path.GetFileNameWithoutExtension(file.FullName));
                                        destFile += ".xoml";
                                        file.CopyTo(destFile, true);
                                    }
                                }
                            }
                        }

                        // AssemblyImageKind directory contains the assemblies and other files required to run this workflow.
                        DirectoryInfo[] assemblyImageKindDirs = parentDir.GetDirectories("AssemblyImageKind");
                        if (assemblyImageKindDirs != null && assemblyImageKindDirs.Length > 0)
                        {
                            DirectoryInfo assemblyImageKindDir = assemblyImageKindDirs[0];
                            foreach (DirectoryInfo subDir in assemblyImageKindDir.GetDirectories())
                            {
                                // "_rels" folder contains the relationship. Skip this,
                                if (!subDir.Name.Equals("_rels", StringComparison.OrdinalIgnoreCase))
                                {
                                    foreach (FileInfo file in subDir.GetFiles())
                                    {
                                        // "ImageBlob.xml" contains the image blob. Skip this,
                                        if (!file.Name.Equals("ImageBlob.xml", StringComparison.OrdinalIgnoreCase))
                                        {
                                            // Copy the assembly image file to destination directory.
                                            string destFile = Path.Combine(destTempPath, file.Name);
                                            file.CopyTo(destFile, true);
                                        }
                                    }
                                }
                            }
                        }

                        // All files have been copied. Read the file paths into a collection.
                        packageFiles = new Collection<string>(Directory.GetFiles(destTempPath).ToList());
                    }
                }
                catch (FileFormatException ex)
                {
                    Logger.Write(ex.Message);
                    if (!silentMode)
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(
                            string.Format(CultureInfo.InvariantCulture, TridentResourceManager.GetString("NotValidPackageFile"), packageFile));
                    }
                }
                catch (Exception ex)
                {
                    Logger.Write(ex.Message);
                    if (!silentMode)
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(
                            string.Format(CultureInfo.InvariantCulture, TridentResourceManager.GetString("NotValidPackageFile"), packageFile));
                    }
                }
                finally
                {
                    if (workflowPackage != null)
                    {
                        // Close the package.
                        workflowPackage.Close();
                    }
                }
            }

            return packageFiles;
        }

        /// <summary>
        /// Extracts the package part specified to the target directory.
        /// </summary>
        /// <param name="packagePart">
        /// Package part to be extracted.
        /// </param>
        /// <param name="targetDirectory">
        /// Target directory.
        /// </param>
        private static void ExtractPart(PackagePart packagePart, string targetDirectory)
        {
            // Create a string with the full path to the target directory.
            string pathToTarget = targetDirectory;

            // Remove leading slash from the Part Uri and make a new Uri from the result
            string stringPart = packagePart.Uri.ToString().TrimStart('/');
            Uri partUri = new Uri(stringPart, UriKind.Relative);

            // Create a full Uri to the Part based on the Package Uri
            string fullPartPath = Path.Combine(pathToTarget, partUri.OriginalString);

            // Create the necessary Directories based on the Full Part Path
            Directory.CreateDirectory(Path.GetDirectoryName(fullPartPath));

            // Create the file with the Part content
            using (FileStream fileStream = new FileStream(fullPartPath, FileMode.Create))
            {
                Helper.CopyStream(packagePart.GetStream(), fileStream);
            }
        }

        /// <summary>
        /// Copies source stream onto target stream.
        /// </summary>
        /// <param name="source">
        /// Source stream.
        /// </param>
        /// <param name="target">
        /// Target stream.
        /// </param>
        private static void CopyStream(Stream source, Stream target)
        {
            if (source != null && target != null)
            {
                byte[] bytes = new byte[source.Length];
                source.Read(bytes, 0, bytes.Length);
                target.Write(bytes, 0, bytes.Length);

                target.Close();
                target.Dispose();
            }
        }
    }

    /// <summary>
    /// Types of Import supported.
    /// </summary>
    public enum ImportType
    {
        None,
        Activity,
        Workflow,
        WorkflowPackage,
        OpenFromFileSystem,
        SaveToRegistry
    }

    /// <summary>
    /// Represents a workflow. Holds all the properties of the workflow.
    /// </summary>
    [Serializable]
    public class WorkflowProperties
    {
        /// <summary>
        /// Name of the workflow.
        /// </summary>
        private string name;

        /// <summary>
        /// Description of the workflow.
        /// </summary>
        private string description;

        /// <summary>
        /// Category identifier of the workflow.
        /// </summary>
        private Guid categoryId;

        /// <summary>
        /// True if the workflow is interactive, False otherwise.
        /// </summary>
        private bool isInteractive;

        /// <summary>
        /// Gets or sets the name of the workflow.
        /// </summary>
        public string Name
        {
            get
            {
                return this.name;
            }

            set
            {
                this.name = value;
            }
        }

        /// <summary>
        /// Gets or sets the description of the workflow.
        /// </summary>
        public string Description
        {
            get
            {
                return this.description;
            }

            set
            {
                this.description = value;
            }
        }

        /// <summary>
        /// Gets or sets the category identifier of the workflow.
        /// </summary>
        public Guid CategoryId
        {
            get
            {
                return this.categoryId;
            }

            set
            {
                this.categoryId = value;
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the workflow is interactive or not.
        /// </summary>
        public bool IsInteractive
        {
            get
            {
                return this.isInteractive;
            }

            set
            {
                this.isInteractive = value;
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public WorkflowProperties()
        {
            this.name = string.Empty;
            this.description = string.Empty;
            this.categoryId = Guid.Empty;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="workflowName">
        /// Name of the workflow.
        /// </param>
        /// <param name="workflowDescription">
        /// Description of the workflow.
        /// </param>
        /// <param name="workflowCategoryId">
        /// Workflow category identifier.
        /// </param>
        /// <param name="interactive">
        /// True if the workflow is interactive, False otherwise.
        /// </param>
        public WorkflowProperties(string workflowName, string workflowDescription, Guid workflowCategoryId, bool interactive)
        {
            this.name = workflowName;
            this.description = workflowDescription;
            this.categoryId = workflowCategoryId;
            this.isInteractive = interactive;
        }
    }

    /// <summary>
    /// Contains metadata of the assembly.
    /// </summary>
    [Serializable]
    public class AssemblyMetadata
    {
        /// <summary>
        /// The name of the assembly.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The name to display.
        /// </summary>
        public string DisplayName { get; set; }

        /// <summary>
        /// The time of import.
        /// </summary>
        public string ImportedTime { get; set; }

        /// <summary>
        /// The user who has imported the file.
        /// </summary>
        public string ImportedBy { get; set; }

        /// <summary>
        /// The Id of the assembly in the registry.
        /// </summary>
        public Guid Id { get; set; }

        /// <summary>
        /// Overriding to string for automation Id purposes.
        /// </summary>
        /// <returns>The string representation of the object.</returns>
        public override string ToString()
        {
            if (!string.IsNullOrEmpty(Name))
            {
                return Name;
            }
            return base.ToString();
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct IMAGE_DOS_HEADER
    {
        [FieldOffset(60)]
        public int e_lfanew;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct IMAGE_NT_HEADERS32
    {
        [FieldOffset(0)]
        public uint Signature;
        [FieldOffset(4)]
        public IMAGE_FILE_HEADER FileHeader;
        [FieldOffset(24)]
        public IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    }

    public struct IMAGE_NT_HEADERS64
    {
        public uint Signature;
        public IMAGE_FILE_HEADER FileHeader;
        public IMAGE_OPTIONAL_HEADER64 OptionalHeader;
    }

    public struct IMAGE_FILE_HEADER
    {
        public ushort Machine;
        public ushort NumberOfSections;
        public ulong TimeDateStamp;
        public ulong PointerToSymbolTable;
        public ulong NumberOfSymbols;
        public ushort SizeOfOptionalHeader;
        public ushort Characteristics;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct IMAGE_OPTIONAL_HEADER32
    {
        [FieldOffset(0)]
        public ushort Magic;
        [FieldOffset(208)]
        public IMAGE_DATA_DIRECTORY DataDirectory;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct IMAGE_OPTIONAL_HEADER64
    {
        [FieldOffset(0)]
        public ushort Magic;
        [FieldOffset(224)]
        public IMAGE_DATA_DIRECTORY DataDirectory;
    }

    public struct IMAGE_DATA_DIRECTORY
    {
        public uint VirtualAddress;
        public uint Size;
    }

    /// <summary>
    /// Status of any operation.
    /// </summary>
    public enum OperationStatus
    {
        /// <summary>
        /// On success.
        /// </summary>
        Success,

        /// <summary>
        /// On failure.
        /// </summary>
        Fail,

        /// <summary>
        /// On user canceled.
        /// </summary>
        UserCancel,

        /// <summary>
        /// No Action required.
        /// </summary>
        NoActionRequired
    }
}